package ferp.android.services;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import ferp.core.ai.tree2.Resolver;
import ferp.core.log.Log;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * User: igorgok
 * Date: 4/8/12 10:38 AM
 */
public class ResolverService2 extends Service
{
  public static final int OK                        =  0;
  public static final int ASSERTION_ERROR           = -1;
  public static final int NULL_POINTER_EXCEPTION    = -2;
  public static final int ARRAY_INDEX_OUT_OF_BOUNDS = -3;
  public static final int OUT_OF_MEMORY             = -4;
  public static final int THROWABLE                 = -5;

  @Override
  public void onCreate()
  {
    super.onCreate();

    Log.debug(TAG, "Resolver service created " + android.os.Process.myPid());
  }

  @Override
  public void onStart(Intent intent, int startId)
  {
    super.onStart(intent, startId);

    exception = null;
    Resolver.ready = false;

    Log.debug(TAG, "Resolver service started");
  }

  /*
  @Override
  public int onStartCommand(Intent intent, int flags, int startId)
  {
    int ret = super.onStartCommand(intent, flags, startId);
    Log.debug(Log.TAG, "sticky: " + ret);
    return ret;
  }
  */

  @Override
  public void onDestroy()
  {
    Log.debug(TAG, "Resolver service destroyed");

    super.onDestroy();
  }

  @Override
  public void onLowMemory()
  {
    Log.debug(TAG, "*** Resolver service low memory ***");

    super.onLowMemory();
  }

  public IBinder onBind(Intent intent)
  {
    Log.debug(TAG, "Resolver service bound");

    exception = null;
    Resolver.ready = false;

    return binder;
  }

  @Override
  public boolean onUnbind(Intent intent)
  {
    Log.debug(TAG, "Resolver service unbound");

    return super.onUnbind(intent);
  }

  @Override
  public void onRebind(Intent intent)
  {
    Log.debug(TAG, "Resolver service rebound");

    super.onRebind(intent);
  }

  private final IResolverService2.Stub binder = new IResolverService2.Stub()
  {
    @Override
    public byte preallocate() throws RemoteException
    {
      Log.debug(TAG, "Resolver service preallocating");

      try
      {
        Resolver.preallocate();

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte initialize(int evaluator, int declarer, int starter, int trump, int[] hand, byte talon0, byte talon1, boolean rostov, boolean open, int sets, int drop) throws RemoteException
    {
      Log.debug(TAG, "Resolver service initializing");

      try
      {
        Resolver.initialize(evaluator, declarer, starter, trump, hand, talon0, talon1, rostov, open, sets, drop);

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte recalculateOnNextSolve(int trick) throws RemoteException
    {
      Log.debug(TAG, "Resolver service changed for standing game");

      try
      {
        Resolver.recalculateOnNextSolve(trick);

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte solve() throws RemoteException
    {
//    For debugging situation, when service process has been killed
/*
      if (true)
      {
        Log.debug(TAG, "Killing resolver process");
        android.os.Process.killProcess(android.os.Process.myPid());
      }
*/

      Log.debug(TAG, "Resolver service solving");

      try
      {
        Resolver.solve();

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte getBestMove(int level, int hand, int trick, int cardsInHand) throws RemoteException
    {
      Log.debug(TAG, "Resolver service getting best move");

      try
      {
        return Resolver.getBestMove(level, hand, trick, cardsInHand);
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public int getBestScore() throws RemoteException
    {
      Log.debug(TAG, "Resolver service getting best score");

      try
      {
        return Resolver.getBestScore();
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte advance(int hand, int trick) throws RemoteException
    {
      Log.debug(TAG, "Resolver service advancing");

      try
      {
        Resolver.advance(hand, trick);

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public long getAvailableMemory() throws RemoteException
    {
      try
      {
        return Resolver.getAvailableMemory();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public int getNodePoolCapacity() throws RemoteException
    {
      try
      {
        return Resolver.getNodePoolCapacity();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public int getMovePoolCapacity() throws RemoteException
    {
      try
      {
        return Resolver.getMovePoolCapacity();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public int getSpeed() throws RemoteException
    {
      try
      {
        return Resolver.getSpeed();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public String getExceptionTrace() throws RemoteException
    {
      if (exception == null)
        return "no service stack trace";

      StringWriter sw = new StringWriter(1024);
      PrintWriter pw = new PrintWriter(sw);

      exception.printStackTrace(pw);
      exception = null;

      return sw.toString();
    }
  };

  private static final String TAG = Log.TAG + "/S2";
  private static Throwable exception;

  private byte report(Throwable e)
  {
    exception = e;

    if (e instanceof AssertionError)
      return ASSERTION_ERROR;

    if (e instanceof NullPointerException)
      return NULL_POINTER_EXCEPTION;

    if (e instanceof ArrayIndexOutOfBoundsException)
      return ARRAY_INDEX_OUT_OF_BOUNDS;

    if (e instanceof OutOfMemoryError)
      return OUT_OF_MEMORY;

    return THROWABLE;
  }
}
